﻿using Castle.DynamicProxy;
using FlyingHat.Common.Attributes;
using FlyingHat.Extensions.Redis;
using FlyingHat.Model.Models.Global;

namespace FlyingHat.Extensions.Aop
{
    /// <summary>
    /// Redis AOP， Service范围
    /// </summary>
    public class RedisCacheAop : IInterceptor
    {
        private readonly IRedisHelper redisHelper;

        public RedisCacheAop(IRedisHelper redisHelper)
        {
            this.redisHelper = redisHelper;
        }
        public void Intercept(IInvocation invocation)
        {
            var method = invocation.MethodInvocationTarget ?? invocation.Method;
            var cache = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute));
            if (cache is CachingAttribute cachingAttribute)
            {
                // 如果不是AOP则跳过
                if (!cachingAttribute.IsAop)
                {
                    invocation.Proceed();
                    return;
                }
                var cacheKey = cachingAttribute.Key;
                if (string.IsNullOrWhiteSpace(cachingAttribute.Key))
                {
                    // 获取动态key
                    cacheKey = CustomCacheKey(invocation);
                }
                var value = redisHelper.Get<IReturnModel>(cacheKey);
                if (value != null)
                {
                    invocation.ReturnValue = (typeof(Task).IsAssignableFrom(method.ReturnType)) ? Task.FromResult(value) : value;
                    return;
                }
                // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑以上是执行方法之前
                // 去执行当前的方法
                invocation.Proceed();
                // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓是执行方法之后
                // 如果是异步，会切换线程进入异步的判断，否则等待通不足方法执行完毕
                // 开始缓存
                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    object? response = null;
                    var type = invocation.Method.ReturnType;
                    if (typeof(Task).IsAssignableFrom(type))
                    {
                        // 如果是异步，就等待执行并获取Result
                        var resultProperty = type.GetProperty("Result");
                        if (resultProperty != null)
                        {
                            response = resultProperty.GetValue(invocation.ReturnValue);
                        }
                    }
                    else
                    {
                        response = invocation.ReturnValue;
                    }
                    if (response != null)
                    {
                        if (cachingAttribute.AbsoluteExpiration > 0)
                        {
                            redisHelper.Set(cacheKey, response, cachingAttribute.AbsoluteExpiration);
                        }
                        else
                        {
                            redisHelper.Set(cacheKey, response);
                        }
                    }
                }
            }
            else
            {
                invocation.Proceed();

            }
        }
        protected static string CustomCacheKey(IInvocation invocation)
        {
            var typeName = invocation.TargetType.Name;
            var methodName = invocation.Method.Name;

            string key = $"{typeName}:{methodName}:";
            if (invocation.Arguments != null)
            {
                foreach (var param in invocation.Arguments)
                {
                    key = $"{key}{param}:";
                }
            }
            return key.TrimEnd(':');
        }

    }
}
